from src.hardware.zlgcan.zlgcan import *
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import threading
import time
import json
from src.globals import g_ic_debugger
from ...utils.utils import singleton
import traceback
import re

from src.utils.utils import PeriodSendThread

GRPBOX_WIDTH = 200
MSGCNT_WIDTH = 50
MSGID_WIDTH = 80
MSGDIR_WIDTH = 60
MSGINFO_WIDTH = 100
MSGLEN_WIDTH = 60
MSGDATA_WIDTH = 200
MSGVIEW_WIDTH = MSGCNT_WIDTH + MSGID_WIDTH + MSGDIR_WIDTH + \
    MSGINFO_WIDTH + MSGLEN_WIDTH + MSGDATA_WIDTH
MSGVIEW_HEIGHT = 400
SENDVIEW_HEIGHT = 125

WIDGHT_WIDTH = GRPBOX_WIDTH + MSGVIEW_WIDTH + 40
WIDGHT_HEIGHT = MSGVIEW_HEIGHT + SENDVIEW_HEIGHT + 20

MAX_DISPLAY = 1000
MAX_RCV_NUM = 10

USBCANFD_TYPE = (41, 42, 43)
USBCAN_XE_U_TYPE = (20, 21, 31)
USBCAN_I_II_TYPE = (3, 4)
###############################################################################


@singleton
class Can_Manual_Tab_Frame(tk.Frame):
    def __init__(self, master, rooter, filter_ids=None):

        super().__init__(master=master)
        # 保存rooter
        self.ROOTER = rooter
        self.BACKEND_MSGREADER = self.ROOTER.BACKEND_MSGREADER
        self.GLOBAL_MSG_QUEUER = self.ROOTER.GLOBAL_MSG_QUEUER

        # 初始化消息队列
        self.can_msg_queue = self.GLOBAL_MSG_QUEUER.create_queue(
            "ZCAN_Demo_Manual_can", "ZCAN_Demo_Manual_can", filter_ids=filter_ids, isCanFD=False)
        self.can_msg_queue.open_queue()

        self.canFD_msg_queue = self.GLOBAL_MSG_QUEUER.create_queue(
            "ZCAN_Demo_Manual_canFD", "ZCAN_Demo_Manual_canFD", filter_ids=filter_ids, isCanFD=True)
        self.canFD_msg_queue.open_queue()

        # 加载设备库信息
        self.json_load_dev_info = None
        with open("./config/dev_info.json", "r") as fd:
            self.json_load_dev_info = json.load(fd)
        if self.json_load_dev_info == None:
            print("device info no exist!")
            return

        # 初始化基础信息
        self.DeviceInit()
        self.WidgetsInit()

        # 自动打开设备
        self.openDev()
        # 自动打开通道channel
        self.openCan()

    def updata_filter(self, filter_ids):
        self.can_msg_queue.update_filter_ids(filter_ids)
        self.canFD_msg_queue.update_filter_ids(filter_ids)

    def DeviceInit(self):
        self.ZCANLIB = self.ROOTER.ZCANLIB
        self._dev_handle = ZLG_CONSTANT.INVALID_DEVICE_HANDLE
        self._can_handle = ZLG_CONSTANT.INVALID_CHANNEL_HANDLE

        # current device info
        self._is_canfd = False
        self._res_support = False
        self.dev_info = None

        # Transmit and receive count display
        self._tx_cnt = 0
        self._rx_cnt = 0
        self._view_cnt = 0

        # read can/canfd message thread

        self.readMsg_show_event = threading.Event()

        self._read_thread = None
        self._lock = threading.RLock()

        # period send var
        self._is_sending = False
        self._id_increase = False
        self._send_num = 1
        self._send_cnt = 1
        self._is_canfd_msg = False
        self._send_msgs = None
        self._send_thread = None

    def readMsg_start(self):
        self.BACKEND_MSGREADER.can_readMsg_thread_open()
        self.readMsg_show_event.set()
        g_ic_debugger("readMsg_start")

        # 一旦启动接收刷新，不能同时清除，否则会报错
        self.btnClrCnt["state"] = tk.DISABLED

    def readMsg_stop(self):
        self.BACKEND_MSGREADER.can_readMsg_thread_close()
        self.readMsg_show_event.clear()
        g_ic_debugger("readMsg_stop")

        # 在停止接收刷新后，才能清除
        self.btnClrCnt["state"] = tk.NORMAL

    def WidgetsInit(self):
        self._dev_frame = tk.Frame(self)
        self._dev_frame.grid(row=0, column=0, padx=2, pady=2, sticky=tk.NSEW)

        self.gbDevInfo = tk.LabelFrame(
            self._dev_frame, height=260, width=GRPBOX_WIDTH, text="设备信息")
        self.gbDevInfo.grid(row=2, column=0, padx=2, pady=2, sticky=tk.NSEW)
        self.gbDevInfo.grid_propagate(0)
        self.DevInfoWidgetsInit()

        self.gbMsgDisplay = tk.LabelFrame(
            self, height=MSGVIEW_HEIGHT, width=MSGVIEW_WIDTH + 12, text="报文显示")
        self.gbMsgDisplay.grid(row=0, column=1, padx=2, pady=2, sticky=tk.NSEW)
        self.gbMsgDisplay.grid_propagate(0)
        self.MsgDisplayWidgetsInit()

        self.gbMsgSend = tk.LabelFrame(
            self, heigh=SENDVIEW_HEIGHT, width=MSGVIEW_WIDTH + 12, text="报文发送")
        self.gbMsgSend.grid(row=2, column=1, padx=2, pady=2, sticky=tk.NSEW)
        self.gbMsgSend.grid_propagate(0)
        self.MsgSendWidgetsInit()

    def DevInfoWidgetsInit(self):
        # Hardware Version
        tk.Label(self.gbDevInfo, anchor=tk.W, text="硬件版本:").grid(
            row=0, column=0, sticky=tk.W)
        self.strvHwVer = tk.StringVar(value='')
        tk.Label(self.gbDevInfo, anchor=tk.W, textvariable=self.strvHwVer).grid(
            row=0, column=1, sticky=tk.W)

        # Firmware Version
        tk.Label(self.gbDevInfo, anchor=tk.W, text="固件版本:").grid(
            row=1, column=0, sticky=tk.W)
        self.strvFwVer = tk.StringVar(value='')
        tk.Label(self.gbDevInfo, anchor=tk.W, textvariable=self.strvFwVer).grid(
            row=1, column=1, sticky=tk.W)

        # Driver Version
        tk.Label(self.gbDevInfo, anchor=tk.W, text="驱动版本:").grid(
            row=2, column=0, sticky=tk.W)
        self.strvDrVer = tk.StringVar(value='')
        tk.Label(self.gbDevInfo, anchor=tk.W, textvariable=self.strvDrVer).grid(
            row=2, column=1, sticky=tk.W)

        # Interface Version
        tk.Label(self.gbDevInfo, anchor=tk.W, text="动态库版本:").grid(
            row=3, column=0, sticky=tk.W)
        self.strvInVer = tk.StringVar(value='')
        tk.Label(self.gbDevInfo, anchor=tk.W, textvariable=self.strvInVer).grid(
            row=3, column=1, sticky=tk.W)

        # CAN num
        tk.Label(self.gbDevInfo, anchor=tk.W, text="CAN通道数:").grid(
            row=4, column=0, sticky=tk.W)
        self.strvCANNum = tk.StringVar(value='')
        tk.Label(self.gbDevInfo, anchor=tk.W, textvariable=self.strvCANNum).grid(
            row=4, column=1, sticky=tk.W)

        # Device Serial
        tk.Label(self.gbDevInfo, anchor=tk.W, text="设备序列号:").grid(
            row=5, column=0, sticky=tk.W)
        self.strvSerial = tk.StringVar(value='')
        tk.Label(self.gbDevInfo, anchor=tk.W, textvariable=self.strvSerial).grid(
            row=6, column=0, columnspan=2, sticky=tk.W)

        # Hardware type
        tk.Label(self.gbDevInfo, anchor=tk.W, text="硬件类型:").grid(
            row=7, column=0, sticky=tk.W)
        self.strvHwType = tk.StringVar(value='')
        tk.Label(self.gbDevInfo, anchor=tk.W, textvariable=self.strvHwType).grid(
            row=8, column=0, columnspan=2, sticky=tk.W)

        # 当前通道
        tk.Label(self.gbDevInfo, anchor=tk.W, text="当前通道号:").grid(
            row=9, column=0, sticky=tk.W)
        self.strvCANChn = tk.StringVar(value='')
        tk.Label(self.gbDevInfo, anchor=tk.W, textvariable=self.strvCANChn).grid(
            row=9, column=1, sticky=tk.W)

    def MsgDisplayWidgetsInit(self):
        # id过滤清单
        self.filter_ids_frame = tk.Frame(self.gbMsgDisplay)
        self.filter_ids_frame.pack(side=tk.TOP)
        tk.Label(self.filter_ids_frame,
                 text="过滤id（0x???格式）").pack(side=tk.LEFT)

        def ids_entry_validator():
            # 在validatecommand中，只能返回True或者False
            # 在这个函数中，更新过滤id清单

            filter_ids_string = self.can_manualTab_filter_ids_entry.get()

            input_filter_ids_string = re.findall(
                r'0x[0-9a-fA-F]{3}', filter_ids_string)

            g_ic_debugger(input_filter_ids_string)
            # 字符串转换为数字
            self.input_filter_ids = [int(canid, 16)
                                     for canid in input_filter_ids_string]

            # g_ic_debugger(self.input_filter_ids)
            # 更新过滤id清单
            self.updata_filter(self.input_filter_ids)

            return True

        self.can_manualTab_filter_ids_entry = tk.Entry(
            self.filter_ids_frame, width=60,  validate="all", validatecommand=ids_entry_validator)

        self.can_manualTab_filter_ids_entry.pack(side=tk.LEFT)
        self.can_manualTab_filter_ids_entry.insert(
            0, '0x2e5,0x5ee,0x716,0x796')

        # 显示treeview
        self._msg_frame = tk.Frame(
            self.gbMsgDisplay, height=MSGVIEW_HEIGHT, width=WIDGHT_WIDTH-GRPBOX_WIDTH+10)
        self._msg_frame.pack(side=tk.TOP)

        self.treeMsg = ttk.Treeview(
            self._msg_frame, height=15, show="headings")
        self.treeMsg["columns"] = (
            "cnt", "id", "direction", "info", "len", "data")

        self.treeMsg.column("cnt",       anchor=tk.CENTER, width=MSGCNT_WIDTH)
        self.treeMsg.column("id",        anchor=tk.CENTER, width=MSGID_WIDTH)
        self.treeMsg.column("direction", anchor=tk.CENTER, width=MSGDIR_WIDTH)
        self.treeMsg.column("info",      anchor=tk.CENTER, width=MSGINFO_WIDTH)
        self.treeMsg.column("len",       anchor=tk.CENTER, width=MSGLEN_WIDTH)
        self.treeMsg.column("data", width=MSGDATA_WIDTH)

        self.treeMsg.heading("cnt", text="序号")
        self.treeMsg.heading("id", text="帧ID")
        self.treeMsg.heading("direction", text="方向")
        self.treeMsg.heading("info", text="帧信息")
        self.treeMsg.heading("len", text="长度")
        self.treeMsg.heading("data", text="数据")

        self.hbar = ttk.Scrollbar(
            self._msg_frame, orient=tk.HORIZONTAL, command=self.treeMsg.xview)
        self.hbar.pack(side=tk.BOTTOM, fill=tk.X)
        self.vbar = ttk.Scrollbar(
            self._msg_frame, orient=tk.VERTICAL, command=self.treeMsg.yview)
        self.vbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.treeMsg.configure(xscrollcommand=self.hbar.set,
                               yscrollcommand=self.vbar.set)

        self.treeMsg.pack(side=tk.LEFT)
        self.treeMsg.selection_set()
        self.btnClrCnt = ttk.Button(
            self.gbMsgDisplay, width=10, text="清空", command=self.BtnClrCnt_Click)
        self.btnClrCnt.pack(side=tk.RIGHT)
        self.btnClrCnt["state"] = tk.DISABLED

        self.strvRxCnt = tk.StringVar()
        self.strvRxCnt.set("0")
        tk.Label(self.gbMsgDisplay, anchor=tk.W, width=5,
                 textvariable=self.strvRxCnt).pack(side=tk.RIGHT)
        tk.Label(self.gbMsgDisplay, width=10, text="接收帧数:").pack(sid=tk.RIGHT)

        self.strvTxCnt = tk.StringVar()
        self.strvTxCnt.set("0")
        tk.Label(self.gbMsgDisplay, anchor=tk.W, width=5,
                 textvariable=self.strvTxCnt).pack(side=tk.RIGHT)
        tk.Label(self.gbMsgDisplay, width=10, text="发送帧数:").pack(side=tk.RIGHT)

        self.readMsg_start_Btn = ttk.Button(
            self.gbMsgDisplay, width=10, text="开始接收", command=self.readMsg_start)
        self.readMsg_start_Btn.pack(side=tk.RIGHT)
        self.readMsg_stop_Btn = ttk.Button(
            self.gbMsgDisplay, width=10, text="停止接收", command=self.readMsg_stop)
        self.readMsg_stop_Btn.pack(side=tk.RIGHT)

    def MsgSendWidgetsInit(self):
        # Send Type
        tk.Label(self.gbMsgSend, anchor=tk.W, text="发送方式:").grid(
            row=0, column=0, sticky=tk.W)
        self.cmbSendType = ttk.Combobox(
            self.gbMsgSend, width=8, state="readonly")
        self.cmbSendType.grid(row=0, column=1, sticky=tk.W)
        self.cmbSendType["value"] = ("正常发送", "单次发送", "自发自收")
        self.cmbSendType.current(0)

        # CAN Type
        tk.Label(self.gbMsgSend, anchor=tk.W, text="帧类型:").grid(
            row=0, column=2, sticky=tk.W)
        self.cmbMsgType = ttk.Combobox(
            self.gbMsgSend, width=6, state="readonly")
        self.cmbMsgType.grid(row=0, column=3, sticky=tk.W)
        self.cmbMsgType["value"] = ("标准帧", "扩展帧")
        self.cmbMsgType.current(0)

        # CAN Format
        tk.Label(self.gbMsgSend, anchor=tk.W, text="帧格式:").grid(
            row=0, column=4, sticky=tk.W)
        self.cmbMsgFormat = ttk.Combobox(
            self.gbMsgSend, width=6, state="readonly")
        self.cmbMsgFormat.grid(row=0, column=5, sticky=tk.W)
        self.cmbMsgFormat["value"] = ("数据帧", "远程帧")
        self.cmbMsgFormat.bind("<<ComboboxSelected>>", self.CmbMsgFormatUpdate)
        self.cmbMsgFormat.current(0)

        # CANFD
        tk.Label(self.gbMsgSend, anchor=tk.W, text="CAN类型:").grid(
            row=0, column=6, sticky=tk.W)
        self.cmbMsgCANFD = ttk.Combobox(
            self.gbMsgSend, width=10, state="readonly")
        self.cmbMsgCANFD.grid(row=0, column=7, sticky=tk.W)
        self.cmbMsgCANFD["value"] = ("CAN", "CANFD", "CANFD BRS")
        self.cmbMsgCANFD.bind("<<ComboboxSelected>>", self.CmbMsgCANFDUpdate)
        self.cmbMsgCANFD.current(0)

        # CAN ID
        tk.Label(self.gbMsgSend, anchor=tk.W, text="帧ID(hex):").grid(
            row=1, column=0, sticky=tk.W)
        self.entryMsgID = tk.Entry(self.gbMsgSend, width=10, text="100")
        self.entryMsgID.grid(row=1, column=1, sticky=tk.W)
        self.entryMsgID.insert(0, "716")

        # CAN Length
        tk.Label(self.gbMsgSend, anchor=tk.W, text="长度:").grid(
            row=1, column=2, sticky=tk.W)
        self.cmbMsgLen = ttk.Combobox(
            self.gbMsgSend, width=6, state="readonly")
        self.cmbMsgLen["value"] = tuple([i for i in range(9)])
        self.cmbMsgLen.current(8)
        self.cmbMsgLen.grid(row=1, column=3, sticky=tk.W)

        # Data
        tk.Label(self.gbMsgSend, anchor=tk.W, text="数据(hex):").grid(
            row=1, column=4, sticky=tk.W)
        self.entryMsgData = tk.Entry(self.gbMsgSend, width=30)
        self.entryMsgData.grid(row=1, column=5, columnspan=4, sticky=tk.W)
        self.entryMsgData.insert(0, "03 22 f1 90")

        # send frame number
        tk.Label(self.gbMsgSend, anchor=tk.W, text="发送帧数:").grid(
            row=2, column=0, sticky=tk.W)
        self.entryMsgNum = tk.Entry(self.gbMsgSend, width=10)
        self.entryMsgNum.grid(row=2, column=1, sticky=tk.W)
        self.entryMsgNum.insert(0, "1")

        # send frame cnt
        tk.Label(self.gbMsgSend, anchor=tk.W, text="发送次数:").grid(
            row=2, column=2, sticky=tk.W)
        self.entryMsgCnt = tk.Entry(self.gbMsgSend, width=8)
        self.entryMsgCnt.grid(row=2, column=3, sticky=tk.W)
        self.entryMsgCnt.insert(0, "1")

        # send frame period
        tk.Label(self.gbMsgSend, anchor=tk.W, text="发送间隔(ms):").grid(
            row=2, column=4, sticky=tk.W)
        self.entryMsgPeriod = tk.Entry(self.gbMsgSend, width=8)
        self.entryMsgPeriod.grid(row=2, column=5, sticky=tk.W)
        self.entryMsgPeriod.insert(0, "0")

        # msg id add
        self.varIDInc = tk.IntVar()
        self.chkbtnIDInc = tk.Checkbutton(
            self.gbMsgSend, text="ID递增", variable=self.varIDInc)
        self.chkbtnIDInc.grid(row=2, column=6, columnspan=2, sticky=tk.W)

        # Send Butten
        self.strvSend = tk.StringVar()
        self.strvSend.set("发送")
        self.btnMsgSend = ttk.Button(
            self.gbMsgSend, textvariable=self.strvSend, command=self.BtnSendMsg_Click)
        self.btnMsgSend.grid(row=3, column=7, padx=2, pady=2)
        self.btnMsgSend["state"] = tk.DISABLED

###############################################################################
# Function
###############################################################################
    def __dlc2len(self, dlc):
        if dlc <= 8:
            return dlc
        elif dlc == 9:
            return 12
        elif dlc == 10:
            return 16
        elif dlc == 11:
            return 20
        elif dlc == 12:
            return 24
        elif dlc == 13:
            return 32
        elif dlc == 14:
            return 48
        else:
            return 64

    def CANMsg2View(self, msg, is_transmit=True):
        view = []
        view.append(str(self._view_cnt))
        self._view_cnt += 1
        view.append(hex(msg.can_id)[2:])
        view.append("发送" if is_transmit else "接收")

        str_info = ''
        str_info += 'EXT' if msg.eff else 'STD'
        if msg.rtr:
            str_info += ' RTR'
        view.append(str_info)
        view.append(str(msg.can_dlc))
        if msg.rtr:
            view.append('')
        else:
            view.append(''.join(hex(msg.data[i])[
                        2:] + ' ' for i in range(msg.can_dlc)))
        return view

    def CANFDMsg2View(self, msg, is_transmit=True):
        view = []
        view.append(str(self._view_cnt))
        self._view_cnt += 1

        view.append(hex(msg.can_id)[2:])
        view.append("发送" if is_transmit else "接收")

        str_info = ''
        str_info += 'EXT' if msg.eff else 'STD'
        if msg.rtr:
            str_info += ' RTR'
        else:
            str_info += ' FD'
            if msg.brs:
                str_info += ' BRS'
            if msg.esi:
                str_info += ' ESI'
        view.append(str_info)
        view.append(str(msg.len))
        if msg.rtr:
            view.append('')
        else:
            view.append(''.join(hex(msg.data[i])[
                        2:] + ' ' for i in range(msg.len)))
        return view

    def UI_MsgReadThreadFunc(self):
        try:
            while self.readMsg_show_event.wait():
                can_num = self.can_msg_queue.count()
                canfd_num = self.canFD_msg_queue.count()

                # g_ic_debugger(can_num, canfd_num)

                if not can_num and not canfd_num:
                    time.sleep(0.005)  # wait 5ms
                    continue

                if can_num:
                    # while can_num and self.readMsg_show_event.wait():
                    if True:

                        can_msgs = list(self.can_msg_queue.deque)
                        self.can_msg_queue.clear()
                        act_num = len(can_msgs)
                        if act_num:
                            # update data
                            self._rx_cnt += act_num
                            self.strvRxCnt.set(str(self._rx_cnt))
                            self.ViewDataUpdate(
                                can_msgs, act_num, False, False)
                        else:
                            break
                        # can_num -= act_num
                        can_num = 0
                if canfd_num:
                    # while canfd_num and self.readMsg_show_event.wait():
                    if True:
                        canfd_msgs = list(self.canFD_msg_queue.deque)
                        self.canFD_msg_queue.clear()

                        act_num = len(canfd_msgs)

                        if act_num:
                            # update data
                            self._rx_cnt += act_num
                            self.strvRxCnt.set(str(self._rx_cnt))
                            self.ViewDataUpdate(
                                canfd_msgs, act_num, True, False)
                        else:
                            break
                        # canfd_num -= act_num
                        canfd_num = 0

        except:
            print(f'[file:{__file__}]=>[line:{(sys._getframe().f_lineno)}]==>{
                  traceback.format_exc()}')

    def ViewDataUpdate(self, msgs, msgs_num, is_canfd=False, is_send=True):
        with self._lock:
            try:
                if is_canfd:
                    for i in range(msgs_num):
                        if len(self.treeMsg.get_children()) == MAX_DISPLAY:
                            self.treeMsg.delete(self.treeMsg.get_children()[0])
                        self.treeMsg.insert(
                            '', 'end', values=self.CANFDMsg2View(msgs[i].frame, is_send))
                        # focus section
                        child_id = self.treeMsg.get_children()[-1]
                        self.treeMsg.focus(child_id)
                        self.treeMsg.selection_set(child_id)
                else:
                    for i in range(msgs_num):
                        if len(self.treeMsg.get_children()) == MAX_DISPLAY:
                            self.treeMsg.delete(self.treeMsg.get_children()[0])
                        self.treeMsg.insert(
                            '', 'end', values=self.CANMsg2View(msgs[i].frame, is_send))
                        # focus section
                        child_id = self.treeMsg.get_children()[-1]
                        self.treeMsg.focus(child_id)
                        self.treeMsg.selection_set(child_id)
            except:
                print(f'[file:{__file__}]=>[line:{(sys._getframe().f_lineno)}]==>{
                      traceback.format_exc()}')

    def PeriodSendIdUpdate(self, is_ext):
        self._cur_id += 1
        if is_ext:
            if self._cur_id > 0x1FFFFFFF:
                self._cur_id = 0
        else:
            if self._cur_id > 0x7FF:
                self._cur_id = 0

    def PeriodSendComplete(self):
        self._is_sending = False
        self.strvSend.set("发送")
        self._send_thread.send_stop()

    def PeriodSend(self):
        # 此处进行发送报文操作
        if self._is_canfd_msg:
            ret = self.ZCANLIB.TransmitFD(
                self._can_handle, self._send_msgs, self._send_num)
        else:
            ret = self.ZCANLIB.Transmit(
                self._can_handle, self._send_msgs, self._send_num)

        # update transmit display
        self._tx_cnt += ret
        self.strvTxCnt.set(str(self._tx_cnt))
        self.ViewDataUpdate(self._send_msgs, ret, self._is_canfd_msg, True)

        if ret != self._send_num:
            self.PeriodSendComplete()
            messagebox.showerror(title="发送报文", message="发送失败！")
            return

        self._send_cnt -= 1
        if self._send_cnt:
            if self._id_increase:
                # 当前ID递增时，更新发送ID
                for i in range(self._send_num):
                    self._send_msgs[i].frame.can_id = self._cur_id
                    self.PeriodSendIdUpdate(self._send_msgs[i].frame.eff)
        else:
            self.PeriodSendComplete()

    def MsgSend(self, msg, is_canfd, num=1, cnt=1, period=0, id_increase=0):
        self._id_increase = id_increase
        self._send_num = num if num else 1
        self._send_cnt = cnt if cnt else 1
        self._is_canfd_msg = is_canfd

        if is_canfd:
            self._send_msgs = (ZCAN_TransmitFD_Data * self._send_num)()
        else:
            self._send_msgs = (ZCAN_Transmit_Data * self._send_num)()

        self._cur_id = msg.frame.can_id
        for i in range(self._send_num):
            self._send_msgs[i] = msg
            self._send_msgs[i].frame.can_id = self._cur_id
            self.PeriodSendIdUpdate(self._send_msgs[i].frame.eff)

        self._is_sending = True
        self._send_thread.send_start(period * 0.001)

    def DevInfoRead(self):
        info = self.dev_info
        if info != None:
            self.strvHwVer.set(info.hw_version)
            self.strvFwVer.set(info.fw_version)
            self.strvDrVer.set(info.dr_version)
            self.strvInVer.set(info.in_version)
            self.strvCANNum.set(str(info.can_num))
            self.strvSerial.set(info.serial)
            self.strvHwType.set(info.hw_type)
            self.strvCANChn.set(str(self._can_handle))

    def DevInfoClear(self):
        self.strvHwVer.set('')
        self.strvFwVer.set('')
        self.strvDrVer.set('')
        self.strvInVer.set('')
        self.strvCANNum.set('')
        self.strvSerial.set('')
        self.strvHwType.set('')
        self.strvCANChn.set('')
###############################################################################
# Event handers
###############################################################################

    def Form_OnClosing(self):

        self.destroy()

    def openDev(self):
        # Open Device
        if self.ROOTER.DEVICE_HANDLE:
            # g_ic_debugger(self.ROOTER.DEVICE_HANDLE)
            self._dev_handle = self.ROOTER.DEVICE_HANDLE
            self.dev_info = self.ZCANLIB.GetDeviceInf(self._dev_handle)
            # g_ic_debugger(self.dev_info)
            self._cur_dev_info = self.json_load_dev_info[self.dev_info.hw_type]
            # g_ic_debugger(self._cur_dev_info)

        else:
            # Open failed
            messagebox.showerror(title="打开设备", message="打开设备失败！")
            return

        # g_ic_debugger('open dev')
        # Update Device Info Display
        self.DevInfoRead()

        # 根据当前的设备类型，更新相关选项信息
        self._is_canfd = self._cur_dev_info["chn_info"]["is_canfd"]
        self._res_support = self._cur_dev_info["chn_info"]["sf_res"]
        if self._is_canfd:
            self.cmbMsgCANFD["value"] = ("CAN", "CANFD", "CANFD BRS")
        else:
            self.cmbMsgCANFD["value"] = ("CAN")

    def openCan(self):

        # FIXME: 如果主界面已经初始化了handle，就直接用主界面的handle
        if self.ROOTER.CHN_HANDLE:
            self._can_handle = self.ROOTER.CHN_HANDLE
        else:
            messagebox.showerror(title="打开通道", message="初始化通道失败!")
            return

        # start send thread，发送报文用线程
        self._send_thread = PeriodSendThread(self.PeriodSend)
        self._send_thread.start()

        # start receive thread

        self._read_thread = threading.Thread(
            None, target=self.UI_MsgReadThreadFunc, name="CAN_Manual_Show_MsgReadThread")
        self._read_thread.start()

        # self.strvCANCtrl.set("关闭")
        self.btnMsgSend["state"] = tk.NORMAL

    def BtnClrCnt_Click(self):
        self._tx_cnt = 0
        self._rx_cnt = 0
        self._view_cnt = 0
        self.strvTxCnt.set("0")
        self.strvRxCnt.set("0")
        # self.treeMsg
        for item in self.treeMsg.get_children():
            self.treeMsg.delete(item)

    def CmbMsgFormatUpdate(self, *args):
        if self.cmbMsgFormat.current() == 0:  # Data Frame
            if self._is_canfd:
                self.cmbMsgCANFD["value"] = ("CAN", "CANFD", "CANFD BRS")
            else:
                self.cmbMsgCANFD["value"] = ("CAN")
                self.cmbMsgCANFD.current(0)
        else:  # Remote Frame
            self.cmbMsgCANFD["value"] = ("CAN")
            self.cmbMsgCANFD.current(0)

    def CmbMsgCANFDUpdate(self, *args):
        tmp = self.cmbMsgLen.current()
        self.cmbMsgLen["value"] = tuple(
            [self.__dlc2len(i) for i in range(16 if self.cmbMsgCANFD.current() else 9)])
        if tmp >= len(self.cmbMsgLen["value"]):
            self.cmbMsgLen.current(len(self.cmbMsgLen["value"]) - 1)

    def BtnSendMsg_Click(self):
        if not self._is_sending:
            is_canfd_msg = True if self.cmbMsgCANFD.current() > 0 else False
            if is_canfd_msg:
                msg = ZCAN_TransmitFD_Data()
            else:
                msg = ZCAN_Transmit_Data()

            msg.transmit_type = self.cmbSendType.current()
            try:
                msg.frame.can_id = int(self.entryMsgID.get(), 16)
            except:
                msg.frame.can_id = 0
            msg.frame.rtr = self.cmbMsgFormat.current()
            msg.frame.eff = self.cmbMsgType.current()

            if not is_canfd_msg:
                msg.frame.can_dlc = self.cmbMsgLen.current()
                msg_len = msg.frame.can_dlc
            else:
                msg.frame.brs = 1 if self.cmbMsgCANFD.current() == 2 else 0
                msg.frame.len = self.__dlc2len(self.cmbMsgLen.current())
                msg_len = msg.frame.len

            data = self.entryMsgData.get().split(' ')
            for i in range(msg_len):
                if i < len(data):
                    try:
                        msg.frame.data[i] = int(data[i], 16)
                    except:
                        msg.frame.data[i] = 0
                else:
                    msg.frame.data[i] = 0

            try:
                msg_num = int(self.entryMsgNum.get())
                msg_cnt = int(self.entryMsgCnt.get())
                period = int(self.entryMsgPeriod.get())
            except:
                msg_num = 1
                msg_cnt = 1
                period = 1
            self.MsgSend(msg, is_canfd_msg, msg_num, msg_cnt,
                         period, self.varIDInc.get())
            self.strvSend.set("停止发送")
        else:
            self.PeriodSendComplete()


if __name__ == "__main__":
    demo = Can_Manual_Tab_Frame()
    demo.mainloop()
